#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SIZE 1024
#define MAX_ARGV 64
#define SEP " "

char env[SIZE];
char*  argv[MAX_ARGV];
char pwd[SIZE];
int lastcode=0;//exit_code

//////////// Redirection Revelent ////////////
#define NoneRedir  -1
#define StdinRedir  0
#define StdoutRedir 1
#define AppendRedir 2

#define STREND '\0'
int redir_type=-1;
char* filename=NULL;


const char* UserName()
{
  char* username=getenv("USER");
  if(username!=NULL)
  {
    return username;
  }
  else
  {
    return "getenv failed";
  }
}

const char* HostName()
{
  char* hostname=getenv("HOSTNAME");
  if(hostname!=NULL)
  {
    return hostname;
  }
  else
  {
    return "getenv failed";
  }
}

char* CurrentWorkDir()
{
  char* cwd=getenv("PWD");
  if(cwd!=NULL)
  {
    return cwd;
  }
  else
  {
    return (char*)"getenv failed";
  }
}

char* HomeDir()
{
    char* hd=getenv("HOME");
    if(hd!=NULL)
    {
      return hd;
    }
    else
    {
      return (char*)"getenv failed";
    }
}

int Interactive(char out[],int size)
{
  printf("[%s@%s %s]$ ",UserName(),HostName(),CurrentWorkDir());
  fgets(out,size,stdin);
  out[strlen(out)-1]='\0';
  return strlen(out);
}


void CheckRedir(char in[])
{
  redir_type=NoneRedir;
  filename=NULL;

  int pos=strlen(in)-1;
  while(pos>=0)
  {
     if(in[pos] == '>')//stdin refirection // append redirection
     {
      //append redirection
      if(in[pos-1] == '>')
      {
        redir_type=AppendRedir;
        in[pos-1]=STREND;
        pos++;
        while(in[pos] == ' ') pos++;
        filename=in+pos;
        break;
      }
      //stdout redirction
      else
      {
        redir_type=StdoutRedir;
        in[pos]=STREND;
        pos++;
        while(in[pos] == ' ') pos++;
        filename=in+pos;
        break;
      }
       
     }
     else if(in[pos] == '<')//stdout redirection
     {
       redir_type=StdinRedir;
       in[pos]=STREND;
       pos++;
       while(in[pos] == ' ') pos++;//jump space
       filename=in+pos;
       break;
     }
     else
     {
       pos--;
     }
  }
}

void SplitCommand(char in[])
{
  CheckRedir(in);
  int i=0;
  argv[i++]=strtok(in,SEP);
  while(argv[i++]=strtok(NULL,SEP));
}

void EXecuteCommand()
{
  pid_t id=fork();
  if(id == 0)
  {
    int fd=-1;
    if(redir_type == StdinRedir)
    {
      fd=open(filename,O_RDONLY);
      dup2(fd,0);
    }
    else if(redir_type == StdoutRedir)
    {
      fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
      dup2(fd,1);
    }
    else if(redir_type == AppendRedir)
    {
      fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
      dup2(fd,1);
    }
    else
    {

    }
    //child process execute command
    execvp(argv[0],argv);
    exit(1);
  }
  int status;
  pid_t rid=waitpid(id,&status,0);
  if(rid>0)
  {
    if(WIFEXITED(status))
    {
      lastcode=WEXITSTATUS(status);
    }
  }
}

int BuildinCmd()
{
  int ret=0;
  if(strcmp(argv[0],"cd")==0)
  {
    ret=1;
    char* target=argv[1];
    if(target==NULL)
    {
      target=HomeDir();
    }
    chdir(target);

    //absolute path
    char temp[SIZE];
    getcwd(temp,SIZE);
    snprintf(pwd,SIZE,"PWD=%s",temp);
    putenv(pwd);
  }
  else if(strcmp(argv[0],"export")==0)
  {
    ret=1;
    if(argv[1]!=NULL)
    {
      strcpy(env,argv[1]);
      putenv(env);
    }
  }
  else if(strcmp("echo",argv[0])==0)
  {
    ret=1;
    if(argv[1]==NULL)
    {
      printf("\n");
    }
    else
    {
      if(argv[1][0]=='$')
      {
        if(argv[1][1]=='?')
        {
          printf("%d\n",lastcode);
          lastcode=0;
        }
        else
        {
          char* e=getenv(argv[1]+1);
          if(e!=NULL)
          {
            printf("%s\n",e);
          }
        }
      }
      else
      {
        printf("%s\n",argv[1]);
      }
    }
  }
  return ret;
}

int main()
{
  while(1)
  {

  char commandline[SIZE];
  int n=Interactive(commandline,SIZE);
  if(n==0)
  {
    continue;
  }
  //printf("%s\n",commandline);
  
  SplitCommand(commandline);
  //for(int i=0;argv[i];i++)
  //{
  //  printf("argv[%d]:%s\n",i,argv[i]);
  //}
  
  int ret=BuildinCmd();
  if(ret)
  {
    continue;
  }

  EXecuteCommand();

  }
  return 0;
}
